Haxe + CreateJS 開発メモ#2 Toolkit for CreateJS連携
前回、Haxe で CreateJS を使用する環境を作りました。引き続き今回は、Flash で作成したシンボルを Toolkit for CreateJS で書きだして、Haxe で使えるようにしてみます。もちろん、Dynamic 型ではなく、クラスを定義してオートコンプリートが効くようにする事を目指します。これができるようになると、いよいよ Flash っぽくなってきますね。
Haxe で JavaScript のライブラリを使用する
Toolkit for CreateJS(長いので以下 TfCJS)に取り掛かる前に、少し回り道になりますが Haxe で JavaScript のライブラリを使用する方法について説明しておきます。
前回、Haxe 内で CreateJS や jQuery を使えるように、既存の Haxe 向けライブラリを haxelib コマンドでインストールしました。これらの有名な JavaScript ライブラリは、Haxe 向けのライブラリも用意されているため、感謝しつつそれらをインストールするだけで済んでしまいます。しかし、いつでもそのような Haxe 向けのライブラリが用意されているわけではありません。もし自分で作成した JavaScript ライブラリを Haxe で使用する場合、Haxe へ対応させる作業は自分で行うことになります。
仮に、以下の様な JavaScript があったとします。
utils.Tracer.js
if (typeof console === "undefined" || console === null) { console = {}; } if (typeof console.log !== "function") { console.log = function () {}; } if(typeof utils === "undefined" || utils === null) { utils = {}; } utils.Tracer = { trace: function(str){ console.log(str); } };
console.log() をラップしただけの、ライブラリと呼ぶのも躊躇われる js ですが、これを Haxe に対応させてみます。と言っても手順は簡単で、JavaScript ライブラリが持つメソッドを記述した extern クラスファイルを作るだけです。
まず JavaScript の名前空間と同じ utils パッケージを作り、その中に JavaScript のオブジェクト名と同じ Tracer クラスを作ります。Tracer クラスの中身には、以下のように記述します。
/src/utils/Tracer.hx
package utils; extern class Tracer { public static function trace(str:String):Void; }
"extern" を付けることがが重要です。このクラスが、実行時に使用可能になるオブジェクトのインターフェイス定義である事を示します。これだけで、Tracer クラスが使用可能になりました。
試しに Main.hx 等で "Tra" とタイプすると補完が出てこなくて焦りますが、"utils" からタイプすると出てきます。
余談ですが、コンソール出力してくれる機能は haxe.Log クラスの trace で実装されているので、自作する必要はありません。
Toolkit for CreateJSで作ったライブラリを使用する
ではこの応用で、 TfCJS から書きだしたライブラリを Haxe に取り込んでみます。まず Flash で、こんなアニメーションを作ってみました。
MovieClipAssets.swf
[SWF]https://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2013/01/MovieClipAssets.swf, 100, 100[/SWF]
これを TfCJS で書きだすと、こんな感じのjsが出力されます。
MovieClipAssets.js
(function (lib, img, cjs) { var p; // shortcut to reference prototypes // stage content: (lib.MovieClipAssets = function() { this.initialize(); // レイヤー 1 this.instance = new lib.SampleMovieClip(); this.instance.setTransform(50,50); this.shape = new cjs.Shape(); this.shape.graphics.f("#999999").s().p("AH0nzIAAPnIvnAAIAAvnIPnAA").cp(); this.shape.setTransform(50,50); this.addChild(this.shape,this.instance); }).prototype = p = new cjs.Container(); p.nominalBounds = new cjs.Rectangle(0,0,100,100); // symbols: (lib.Tween1 = function() { this.initialize(); // レイヤー 1 this.shape = new cjs.Shape(); this.shape.graphics.f("#000000").s().p("AhjhjIDHAAIAADHIjHAAIAAjH").cp(); this.addChild(this.shape); }).prototype = p = new cjs.Container(); p.nominalBounds = new cjs.Rectangle(-9.9,-9.9,20,20); (lib.SampleMovieClip = function(mode,startPosition,loop) { this.initialize(mode,startPosition,loop,{},true); // レイヤー 1 this.instance = new lib.Tween1("synched",0); this.timeline.addTween(cjs.Tween.get(this.instance).to({rotation:360},49).wait(1)); }).prototype = p = new cjs.MovieClip(); p.nominalBounds = new cjs.Rectangle(-9.9,-9.9,20,20); })(lib = lib||{}, images = images||{}, createjs = createjs||{}); var lib, images, createjs;
この js では、Flash のステージコンテンツに対応している MovieClipAssets オブジェクトと、作成したシンボル "SampleMovieClip" に対応した SampleMovieClip オブジェクト、また、その中で使われている Tween1 オブジェクトが、名前空間 lib の中に定義されています。
では、この js ファイルをプロジェクトの bin ディレクトリにコピーした後、先ほどの JavaScript ライブラリをHaxeに対応させる手順に則って、これらに対応する extern クラスを lib パッケージ内に作成していきます。
/src/lib/MovieClipAssets.hx
package lib; import createjs.easeljs.Container; extern class MovieClipAssets extends Container { }
/src/lib/SampleMovieClip.hx
package lib; import createjs.easeljs.MovieClip; extern class SampleMovieClip extends MovieClip { }
シンボル独自のメソッドやプロパティは持たないため、それぞれに対応する CreateJS のクラスの継承だけで事足ります。
ただし、現在 haxelib に登録されている CreateJS-Haxe(v1.1) の MovieClip クラスにはバグがあるようで、このままコンパイルするとエラーになってしまいます。Github からバグ修正済みの MovieClip.hx をダウンロードして差し替える必要があります。
https://github.com/nickalie/CreateJS-Haxe/blob/master/createjs/easeljs/MovieClip.hx
差し替える対象のファイルは、Haxe インストールディレクトリの下の \lib\createjs\1,1\createjs\easeljs に入っています。
それでは早速、実際に作ったクラスを使ってみます。
/bin/index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>CreateJSTest</title> <meta name="description" content="" /> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> <script src="http://code.createjs.com/easeljs-0.5.0.min.js"></script> <script src="http://code.createjs.com/tweenjs-0.3.0.min.js"></script> <script src="http://code.createjs.com/movieclip-0.5.0.min.js"></script> </head> <body> <script src="NewProject.js"></script> <script src="MovieClipAssets.js"></script> <canvas id="canvas" width="250" height="250"></canvas> </body> </html>
/src/Main.hx
package ; import createjs.easeljs.Stage; import createjs.easeljs.Ticker; import js.JQuery; import js.Lib; import lib.MovieClipAssets; import lib.SampleMovieClip; class Main { private var stage:Stage; //エントリポイント public static function main():Void{ new Main(); } //コンストラクタ public function new() { new JQuery(Lib.document).ready(function(e:JqEvent):Void { stage = new Stage(cast Lib.document.getElementById("canvas")); //MovieClipAssetsをステージに追加 var mca:MovieClipAssets = new MovieClipAssets(); stage.addChild(mca); //SampleMovieClipをステージに追加 for (i in 0...20) { var smc:SampleMovieClip = new SampleMovieClip("independent", 0, true, null); stage.addChild(smc); smc.x = 250 * Math.random(); smc.y = 250 * Math.random(); } //24fpsでstageを描画更新する Ticker.setFPS(24); Ticker.addListener(stage); }); } }
実行結果
Flash で作ったステージコンテンツ(グレーの四角)をまるごと使うことも、シンボル(回転する黒い四角)ごとに個別に使う事もできます。Flash コンテンツを TfCJS で書き出す際には色々と制限があり、Flash で作ったものを何でも書き出せるわけではありませんが、静止画像やシンプルなアニメーション程度なら全く問題はありません。
注意点
TfCJS は、デフォルトで lib, images, createjs の各名前空間を使用するため、もし Haxe 側でこれらと競合するパッケージに extern ではないクラスを配置すると、Haxe が作るレキシカルスコープ内のパッケージオブジェクトに、TfCJS のグローバルスコープのオブジェクトが隠蔽されてしまい、コンパイルは通りますが実行時エラーが発生してしまいます。entern クラスのパッケージの最上位を window にする事でも回避可能ですが、あまりスマートなやり方ではない気がします。
TfCJS の設定で、出力される js で使用する名前空間を変更する事が可能ですので、もし競合する場合は必要に応じて設定を変えましょう。個人的には、TfCJS が使用する名前空間 lib と、Haxe で JavaScript のオブジェクトにアクセスするためのクラス js.Lib がオートコンプリート時に紛らわしいので、別の名前空間に変更したほうが良いと思います。
今回参考にさせて頂いたサイト様
CreateJS & Haxe : MovieClip.hx, Timeline.hx, Point.hx 修正・処理追加
http://www.dango-itimi.com/blog/archives/2013/001151.html